
/*
 * Copyright (C) 2008-2009 Archie L. Cobbs. All rights reserved.
 *
 * $Id$
 */

package org.dellroad.sidekar.schema;

import java.util.Collections;
import java.util.List;
import java.util.Set;

import org.dellroad.sidekar.schema.change.AddEntityChange;
import org.dellroad.sidekar.schema.change.AddFieldChange;
import org.dellroad.sidekar.schema.change.AddIndexChange;
import org.dellroad.sidekar.schema.change.SchemaChange;
import org.dellroad.sidekar.schema.change.SchemaMutator;
import org.dellroad.sidekar.schema.model.EntityModel;
import org.dellroad.sidekar.schema.model.FieldModel;
import org.dellroad.sidekar.schema.model.IndexModel;
import org.dellroad.sidekar.schema.model.SchemaModel;
import org.dellroad.stuff.graph.TopologicalSorter;

/**
 * Builds a schema from scratch using step-by-step {@link SchemaChange} operations.
 */
public class SchemaBuilder {

    private final SchemaModel schemaModel;

    /**
     * Constructor.
     *
     * @param schemaModel the desired schema to build
     */
    public SchemaBuilder(SchemaModel schemaModel) {
        if (schemaModel == null)
            throw new IllegalArgumentException("null schemaModel");
        this.schemaModel = schemaModel;
    }

    /**
     * Build the configured {@link SchemaModel}.
     *
     * <p>
     * This constructs the {@link SchemaModel} configured in the construtor from scratch (i.e., assuming an initially
     * empty schema), by applying a series of incremental {@link SchemaChange} operations to the given {@link SchemaMutator}.
     *
     * @param schemaMutator object to apply incremental schema changes to
     */
    public void buildSchema(SchemaMutator schemaMutator) {

        // Order entities so that parents appear before children
        TopologicalSorter.EdgeLister<EntityModel> edgeLister = new TopologicalSorter.EdgeLister<EntityModel>() {
            @Override
            public Set<EntityModel> getOutEdges(EntityModel entityModel) {
                return entityModel.getParentModel() != null ?
                  Collections.<EntityModel>singleton(entityModel.getParentModel()) : Collections.<EntityModel>emptySet();
            }
        };
        List<EntityModel> entityList = new TopologicalSorter<EntityModel>(this.schemaModel.getEntityModels().values(),
          edgeLister).sortEdgesReversed();

        // Add all entities
        for (EntityModel entityModel : entityList) {
            String parentName = entityModel.getParentModel() != null ? entityModel.getParentModel().getName() : null;
            schemaMutator.schemaChange(new AddEntityChange(entityModel.getName(), parentName, entityModel.isAbstract()));
        }

        // Add all fields
        for (EntityModel entityModel : entityList) {
            for (FieldModel fieldModel : entityModel.getFieldModels().values())
                schemaMutator.schemaChange(new AddFieldChange(entityModel.getName(), fieldModel));
        }

        // Add all indexes
        for (EntityModel entityModel : entityList) {
            for (IndexModel indexModel : entityModel.getIndexModels().values())
                schemaMutator.schemaChange(new AddIndexChange(entityModel.getName(), indexModel));
        }
    }
}

